package cn.jiedanba.itext5.rsa.signHash;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;

import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalBlankSignatureContainer;
import com.itextpdf.text.pdf.security.ExternalSignatureContainer;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import com.itextpdf.text.pdf.security.SecurityIDs;
import com.itextpdf.text.pdf.security.TSAClient;

import cn.jiedanba.itext5.BCProvider;
import cn.jiedanba.itext5.gm.sign.vo.GetPdfHash;
import cn.jiedanba.itext5.gm.sign.vo.GetPdfHashParamVo;

/**
 * IText pdf分离式签名 . 场景：从外部设备获取p1数据，例如ukey，签验签服务器，KMS系统
 * 
 * @author dell
 *
 */
public class ITextSignHashUtil extends BCProvider {
	/**
	 * 创建签名域，并获取摘要值
	 * 
	 * @param param
	 */
	public static GetPdfHash getPdfHash(GetPdfHashParamVo param) {
		try {
			ByteArrayOutputStream tmpos = new ByteArrayOutputStream();
			PdfReader reader = new PdfReader(param.getPdf());
			PdfStamper stamper = PdfStamper.createSignature(reader, tmpos, '\0', null, true);
			PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
			appearance.setVisibleSignature(
					new Rectangle(param.getLlx(), param.getLly(), param.getUrx(), param.getUry()), param.getPageNo(),
					appearance.getNewSigName());
			appearance.setSignatureGraphic(Image.getInstance(param.getSignImage()));
			appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
			appearance.setRenderingMode(RenderingMode.GRAPHIC);
			appearance.setReason(param.getReason());
			appearance.setLocation(param.getLocation());
			ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE,
					PdfName.ADBE_PKCS7_DETACHED);
			MakeSignature.signExternalContainer(appearance, external, new Integer(8192 * 2 + 2));
			InputStream data = appearance.getRangeStream();

			MessageDigest externalDigest = MessageDigest.getInstance(param.getHashAlgorithm(), BC);
			byte digest[] = DigestAlgorithms.digest(data, externalDigest);

			ASN1EncodableVector attribute = new ASN1EncodableVector();
			ASN1EncodableVector v = new ASN1EncodableVector();
			v.add(new ASN1ObjectIdentifier(SecurityIDs.ID_CONTENT_TYPE));
			v.add(new DERSet(new ASN1ObjectIdentifier(SecurityIDs.ID_PKCS7_DATA)));
			attribute.add(new DERSequence(v));
			v = new ASN1EncodableVector();
			v.add(new ASN1ObjectIdentifier(SecurityIDs.ID_MESSAGE_DIGEST));
			v.add(new DERSet(new DEROctetString(digest)));
			attribute.add(new DERSequence(v));

			byte[] sh = new DERSet(attribute).getEncoded(ASN1Encoding.DER);
			GetPdfHash returnVo = new GetPdfHash();
			returnVo.setSignHash(sh);
			returnVo.setDigesHash(digest);
			returnVo.setEmptySignaturePdf(tmpos.toByteArray());
			returnVo.setFieldName(appearance.getFieldName());
			return returnVo;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * 签署p7
	 * 
	 * @param digest
	 *            pdf原文摘要值
	 * @param p1
	 *            私钥签名数据
	 * @param chain
	 *            证书链
	 * @param hashAlgorithm
	 *            摘要算法
	 * @param tsaClient
	 *            时间戳
	 * @return
	 * @throws GeneralSecurityException
	 */
	public static byte[] signHash(byte[] digest, byte[] p1, Certificate[] chain, String hashAlgorithm,
			TSAClient tsaClient) throws GeneralSecurityException {
		PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, null, false);
		sgn.setExternalDigest(p1, null, "RSA");
		return sgn.getEncodedPKCS7(digest, tsaClient, null, null, CryptoStandard.CMS);
	}
}
